﻿using System;
using System.Runtime.InteropServices;
using Testing.Helpers.Assertion;

namespace Testing.Helpers.Computer
{
    public static class ChangeComputerDateTimeHelper
    {
        /// <summary> This structure represents a date and time. </summary>
        private struct SYSTEMTIME
        {
            public ushort wYear, wMonth, wDayOfWeek, wDay,
               wHour, wMinute, wSecond, wMilliseconds;
        }

        /// <summary>
        /// This function retrieves the current system date
        /// and time expressed in Coordinated Universal Time (UTC).
        /// </summary>
        /// <param name="lpSystemTime">[out] Pointer to a SYSTEMTIME structure to
        /// receive the current system date and time.</param>
        [DllImport("kernel32.dll")]
        private extern static void GetSystemTime(ref SYSTEMTIME lpSystemTime);

        /// <summary>
        /// This function sets the current system date
        /// and time expressed in Coordinated Universal Time (UTC).
        /// </summary>
        /// <param name="lpSystemTime">[in] Pointer to a SYSTEMTIME structure that
        /// contains the current system date and time.</param>
        [DllImport("kernel32.dll")]
        private extern static uint SetSystemTime(ref SYSTEMTIME lpSystemTime);

        /// <summary>
        /// Sets current computer datetime.
        /// </summary>
        /// <param name="dateTime">New current computer datetime.</param>
        /// <returns>Timespan between current and new current computer datetime.</returns>
        public static TimeSpan SetComputerDate(DateTime dateTime)
        {
            var startDateTime = DateTime.Now;
            TimeSpan addTimeSpan = dateTime.Subtract(startDateTime);
            SetComputerDateTime(dateTime);
            return addTimeSpan;
        }

        /// <summary>
        /// Sets current time to the present day.
        /// </summary>
        /// <param name="currentTimeSpan">New time.</param>
        /// <returns>Timespan between current and new current computer datetime.</returns>
        public static TimeSpan SetComputerTime(TimeSpan currentTimeSpan)
        {
            var startDateTime = DateTime.Now;
            var currentDateTime = new DateTime(startDateTime.Year, startDateTime.Month,
                startDateTime.Day, currentTimeSpan.Hours, currentTimeSpan.Minutes, currentTimeSpan.Seconds);
            TimeSpan addTimeSpan = currentDateTime.Subtract(startDateTime);
            SetComputerDateTime(currentDateTime);
            return addTimeSpan;
        }

        /// <summary>
        /// Sets current computer datetime.
        /// </summary>
        /// <param name="dateTime">New current computer datetime.</param>
        public static void SetComputerDateTime(DateTime dateTime)
        {
            DateTime universalTime = dateTime.ToUniversalTime();
            var st = new SYSTEMTIME
            {
                wYear = (ushort)universalTime.Year,
                wMonth = (ushort)universalTime.Month,
                wDay = (ushort)universalTime.Day,
                wHour = (ushort)universalTime.Hour,
                wMinute = (ushort)universalTime.Minute,
                wSecond = (ushort)universalTime.Second,
                wMilliseconds = (ushort)universalTime.Millisecond,
                wDayOfWeek = (ushort)universalTime.DayOfWeek
            };
            if (SetSystemTime(ref st) == 0)
                throw new TestExecutionException("Не получилось установить локальное время компьютера.");
        }
    }
}
